package com.wh.minio.container.common;

import io.minio.MinioClient;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.messages.Item;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.MediaType;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/***
 * wh-minio工具类
 * @Package com.wh.minio.container.common
 * @author 谢泽鹏
 * @date 2016年11月07日 下午15:10
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2016
 * @version V1.0
 */
public class MinioClientUtils {


    private static final Log logger = LogFactory.getLog(MinioClientUtils.class);

    /**
     * 单例对象
     */
    private static MinioClientUtils minioClientUtils;

    /**
     * minioClient对象
     */
    private MinioClient minioClient;

    /**
     * 重试次数
     */
    private static int RETRY_NUM = 3;


    /**
     * 单例模式
     * @return
     */
    public static MinioClientUtils getInstance() {
        if (null != minioClientUtils) {
            return minioClientUtils;
        }
        synchronized (MinioClientUtils.class) {
            if (null == minioClientUtils) {
                minioClientUtils = new MinioClientUtils();
            }
        }
        return minioClientUtils;
    }


    /**
     * 初始化
     */
    private MinioClientUtils() {
        try {
            //加载属性文件
            ClassLoader classLoader = MinioClientUtils.class.getClassLoader();
            PropertiesUtils.setFileName("minio.properties");
            PropertiesUtils.initProperties(classLoader);
            Constants.endpoint  = PropertiesUtils.getPropertiesByKey("wh.minio.endpoint");
            Constants.port  = Integer.parseInt(PropertiesUtils.getPropertiesByKey("wh.minio.port"));
            Constants.secretKey = PropertiesUtils.getPropertiesByKey("wh.minio.secretKey");
            Constants.accessKey = PropertiesUtils.getPropertiesByKey("wh.minio.accessKey");
            minioClient = new MinioClient(Constants.endpoint, Constants.port, Constants.accessKey, Constants.secretKey, false);

            //minioClient = new MinioClient(Constants.endpoint, Constants.accessKey, Constants.secretKey, false);
            minioClient.setTimeout(TimeUnit.SECONDS.toMillis(15), TimeUnit.SECONDS.toMillis(15), TimeUnit.SECONDS.toMillis(30));
        } catch (Exception e) {
            logger.error("restClient.close occur Exception: " + e.getMessage());
        }
    }


    public MinioClient getMinioClient() {
        return minioClient;
    }

    /**
     * 判断桶bucket是否存在
     * @param bucket 桶的名称
     * @return
     */
    public  boolean isExistbucket(String bucket){
        boolean falg = false;
        try{
            falg = minioClient.bucketExists(bucket);
        } catch (Exception e){
            falg = false;
            logger.error("isExistbucket Exception: "  + e.getMessage());
        }
            return  falg;
    }

    /**
     * 创建桶bucket
     * @param bucket
     * @return
     */
    public boolean createBucket(String bucket) {
        boolean isCreated;
        try {
            minioClient.makeBucket(bucket);
            isCreated = true;
        } catch (Exception e) {
            isCreated = false;
            logger.error("createBucket Exception: " + e.getMessage() );
        }
            return isCreated;
    }

    /**
     * 删除桶，先删除桶内资源，再删除桶
     * @param bucketName 桶名称
     * @return
     * @throws IOException
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws InsufficientDataException
     * @throws InternalException
     * @throws NoResponseException
     * @throws InvalidBucketNameException
     * @throws XmlPullParserException
     * @throws ErrorResponseException
     * @throws InvalidArgumentException
     */
    public boolean removeBucket(String bucketName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException, InvalidArgumentException {
        for (Result<Item> result:minioClient.listObjects(bucketName)) {
            Item item = result.get();
            minioClient.removeObject(bucketName, item.objectName());
        }
        minioClient.removeBucket(bucketName);
        return true;
    }


    /**
     * 在桶中判断filePath是否存在
     * @param bucket      桶的名称
     * @param filePath    桶的文件路径
     * @return
     * @throws Exception
     */
    public  boolean filePathExist(String bucket, String filePath) throws  Exception{
        boolean falg = false;
        Iterable<Result<Item>> myObjects = getInstance().getMinioClient().listObjects(bucket);
        for (Result<Item> result : myObjects) {
            Item item = result.get();
            if(item.objectName().equals(filePath)){
                falg = true;
                break;
            }
        }
        return falg;
    }


    /**
     * 上传jpeg文件
     * @param bucket         桶的名称
     * @param minioPath      minio路径
     * @param jpegFilePath   jpeg文件路径
     * @return
     */
    public String uploadJpegFile(String bucket, String minioPath, String jpegFilePath) {
        return uploadFile(bucket, minioPath, jpegFilePath, MediaType.IMAGE_JPEG_VALUE);
    }


    /**
     * 上传jpeg文件(通过inputSteam)
     * @param bucket         桶的名称
     * @param minioPath      minio路径
     * @param inputStream    jpeg文件流
     * @return
     */
    public String uploadJpegStream(String bucket, String minioPath, InputStream inputStream) {
        return uploadStream(bucket, minioPath, inputStream, MediaType.IMAGE_JPEG_VALUE);
    }


    /**
     * 上传文件
     * @param bucket          桶的名称
     * @param minioPath       minio路径
     * @param localFile       文件路径
     * @param mediaType       MediaType类型
     * @return
     */
    public String uploadFile(String bucket, String minioPath, String localFile, String mediaType) {
        try {
            logger.info("uploadFile for bucket = " + bucket + ", minioPath = " + minioPath + ", localFile = " + localFile + ", mediaType = " +  mediaType);
            if (StringUtils.isBlank(mediaType)) {
                mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
            }
            putObjectWithRetry(bucket, minioPath, localFile, mediaType);
            return cleanUrlByRemoveIp(minioClient.getObjectUrl(bucket, minioPath));
        } catch (Exception e) {
            logger.error("uploadFile occur error:", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传文件(通过inputSream)
     * @param bucket          桶的名称
     * @param minioPath       minio路径
     * @param inputStream     文件输入流
     * @param mediaType       MediaType类型
     * @return
     */
    public String uploadStream(String bucket, String minioPath, InputStream inputStream, String mediaType) {
        try {
            logger.info("uploadStream for bucket = " + bucket + ", minioPath = " + minioPath + ", inputStream = " + inputStream + ", mediaType = " +  mediaType);
            if (StringUtils.isBlank(mediaType)) {
                mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
            }
            putObjectWithRetry(bucket, minioPath, inputStream, mediaType);
            return cleanUrlByRemoveIp(minioClient.getObjectUrl(bucket, minioPath));
        } catch (Exception e) {
            logger.error("uploadStream occur error:", e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 存放文件资源
     * @param bucket         桶的名称
     * @param objectName     桶中的对象名称
     * @param fileName       文件名称
     * @param contentType    文件类型
     * @throws InvalidBucketNameException
     * @throws NoSuchAlgorithmException
     * @throws InsufficientDataException
     * @throws IOException
     * @throws InvalidKeyException
     * @throws NoResponseException
     * @throws XmlPullParserException
     * @throws ErrorResponseException
     * @throws InternalException
     * @throws InvalidArgumentException
     * @throws InsufficientDataException
     */
    public void putObjectWithRetry(String bucket, String objectName, String fileName, String contentType) throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException, InternalException, InvalidArgumentException, InsufficientDataException {
        int current = 0;
        boolean isSuccess = false;
        while (!isSuccess && current < RETRY_NUM) {
            try {
                minioClient.putObject(bucket, objectName, fileName, contentType);
                isSuccess = true;
                logger.info("putObject file, bucket = " + bucket + ", objectName = " + objectName + ", success !");
            } catch (ErrorResponseException e) {
                current++;
                logger.error("putObject file, ErrorResponseException occur!");
            }
        }
        if (current == RETRY_NUM) {
            logger.warn("putObject file, bucket = " + bucket + ", objectName = " + objectName + ", failed finally!");
        }
    }


    /**
     * 存放文件资源(inputStream)
     * @param bucket         桶的名称
     * @param objectName     桶中的对象名称
     * @param inputStream    文件流
     * @param contentType    文件类型
     * @throws IOException
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws InsufficientDataException
     * @throws InvalidArgumentException
     * @throws NoResponseException
     * @throws InvalidBucketNameException
     * @throws XmlPullParserException
     * @throws InternalException
     */
    public void putObjectWithRetry(String bucket, String objectName, InputStream inputStream, String contentType) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidArgumentException, NoResponseException, InvalidBucketNameException, XmlPullParserException, InternalException {
        int current = 0;
        boolean isSuccess = false;
        while (!isSuccess && current < RETRY_NUM) {
            try {
                minioClient.putObject(bucket, objectName, inputStream, contentType);
                isSuccess = true;
                logger.info("putObject inputStream, bucket = " + bucket + ", objectName = " + objectName + ", success !");
            } catch (ErrorResponseException e) {
                current++;
                logger.error("putObject inputStream, ErrorResponseException occur for time = " + current, e);
            }
        }
        if (current == RETRY_NUM) {
            logger.warn("putObject inputStream, bucket = " + bucket + ", objectName = " + objectName + ", failed finally!");
        }
    }

    private String cleanUrlByRemoveIp(String objectUrl) {

        return  null;
    }


    public Iterable<Result<Item>> listFiles(String bucketName, String prefix, boolean recursive) {
        logger.info("list files for bucketName={} prefix={} recursive={}" +  bucketName + prefix  + recursive);
        return minioClient.listObjects(bucketName, prefix, recursive);
    }


//    public List<MinioEntity> listFilesByBucketNameSwap(String bucketName) {
//        logger.info("listFilesByBucketName for bucketName={}" +  bucketName);
//        return swapResultToEntityList(minioClient.listObjects(bucketName, null, true));
//    }

    public Iterable<Result<Item>> listFilesByBucketName(String bucketName) {
        logger.info("listFilesByBucketName for bucketName={}" + bucketName);
        return minioClient.listObjects(bucketName, null, true);
    }

    public Iterable<Result<Item>> listFilesByBucketAndPrefix(String bucketName, String prefix) {
        logger.info("listFilesByBucketAndPrefix for bucketName={} and prefix={}" + bucketName + prefix);
        return minioClient.listObjects(bucketName, prefix, true);
    }

//    public List<MinioEntity> listFilesByBucketAndPrefixSwap(String bucketName, String prefix) {
//        logger.info("listFilesByBucketAndPrefix for bucketName={} and prefix={}" +  bucketName +  prefix);
//        return swapResultToEntityList(minioClient.listObjects(bucketName, prefix, true));
//    }
//
//    private List<MinioEntity> swapResultToEntityList(Iterable<Result<Item>> results) {
//        List<MinioEntity> minioEntities = new ArrayList<>();
//        for (Result<Item> result : results) {
//            minioEntities.add(swapResultToEntity(result));
//        }
//        return minioEntities;
//    }
//
//
//    private MinioEntity swapResultToEntity(Result<Item> result) {
//        MinioEntity minioEntity = new MinioEntity();
//        try {
//            if (result.get() != null) {
//                Item item = result.get();
//                minioEntity.setObjectName(cleanUrlByRemoveIp(item.objectName()));
//                minioEntity.setDir(item.isDir());
//                minioEntity.setEtag(item.etag());
//                minioEntity.setLastModified(item.lastModified());
//                minioEntity.setSize(item.size());
//                minioEntity.setStorageClass(item.storageClass());
//            }
//        } catch (Exception e) {
//            logger.error("UrlUtils error, e={}" + e.getMessage());
//        }
//        return minioEntity;
//    }

}
